/**
 * TODO: Copyright info
 */

#ifndef _HDPLANNING_HPP_
#define _HDPLANNING_HPP_

#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
#include <string>
#include "Common.hpp"

namespace Navinfo {
namespace Engine {
namespace Planning {

typedef uint32_t HDRouteID; ///< Represent a HD route. 0 means invalid.

/**
 * @brief Basic unit for an avoid area.
 * 
 */
struct AvoidAreaUnit
{
    PosRectangle m_AreaRect;
    bool         m_Exclude_Motorway;  ///< Whether or not the motorway should be excluded.
};

/**
 * @brief Avoid area with an ID.
 * 
 */
struct AvoidArea
{
    uint32_t m_ID;
    AvoidAreaUnit m_Area;
};

typedef std::vector<AvoidArea> AvoidAreaList; ///< A list for avoid area.

/**
 * @brief A segment in a route.
 * 
 */
struct HDRouteSegment
{
    enum Status
    {
        HDROUTE_SEGMENT_STATUS_UNKNOWN = 0,       ///< Default value, means nothing.
        HDROUTE_SEGMENT_STATUS_SUCCESS,           ///< A normal route segment.
        HDROUTE_SEGMENT_STATUS_NO_DATA,           ///< This segment has no data.
        HDROUTE_SEGMENT_STATUS_PLANNING_FAILED    ///< This segment is failed during planning.
    };
    Status m_SegmentStatus;                       ///< Status of this segment.
    std::vector<DirectedLinkID> m_SegmentLinks;   ///< Link sequence within this segment.
    uint32_t m_FirstLinkLength;                   ///< Valid length of first link, from start point to end of link(travel direction).
    uint32_t m_LastLinkLength;                    ///< Valid length of last link, from start of link to end point(travel direction).
    Pos3D m_NoDataStartPoint;                     ///< If this segment has no data, represent start point.
    Pos3D m_NoDataEndPoint;                       ///< If this segment has no data, represent end point.
};


/**
 * @brief Store a HD route. A route contains multiple segments with individual status.
 * 
 */
struct HDRouteInfo
{
    HDRouteID m_RouteID;
    std::vector<HDRouteSegment> m_Segments;
};



struct HDRoutePlanningResult
{
    enum FailedReason
    {
        FAILED_REASON_UNKNOWN = 0,     ///< default value, means nothing
		FAILED_REASON_STOPPED          ///< stopped by request
    };
    bool m_Success;                    ///< whether or not the planning succeeded
    FailedReason m_Reason;             ///< if failed, the reason for failure
};

struct HDRoutePlanningOptions
{
    bool m_Motorway;
    bool m_TollRoad;
    bool m_Ferry;
    bool m_Tunnel;
    bool m_AvoidArea;
    bool m_TimeRestrict;
};

struct WayPoint
{
    enum PointType
    {
        POINT_TYPE_UNKNOWN = 0,         ///< default value, means nothing.
        POINT_TYPE_START,               ///< a point that route must start from.
        POINT_TYPE_DESTINATION,         ///< a point that means destination.
        POINT_TYPE_VIA                  ///< a point that route must pass through.
    };
    PointType    m_Type;                ///< type of this point
    Pos3D        m_Point;               ///< geo position of this point
};

/**
 * @brief Import a SD route with OpenLR and convert to HD route.
 * 
 */
class HDRoutePlanning
{
protected:
    virtual ~HDRoutePlanning() {};

public:
    /**
     * @brief Import a SD route(OpenLR) encoding with protobuf stream.
     * 
     * @param InputStream   Protobuf3 stream
     * @return HDRouteID    Represent a HD route
     */
    virtual HDRouteID ImportOpenLR(const std::vector<uint8_t> &InputStream) = 0;

    /**
     * @brief Import a SD route(CrossReference) encoding with protobuf stream.
     * 
     * @param InputStream   Protobuf3 stream
     * @return HDRouteID    Represent a HD route
     */
    virtual HDRouteID ImportCrossRef(const std::vector<uint8_t> &InputStream) = 0;

    /**
     * @brief Import a custom format encoding stream. This is for TEST PURPOSE ONLY.
     * 
     * @param InputStream   Custom stream
     * @return HDRouteID    Represent a HD route
     */
    virtual HDRouteID ImportCustom(const std::vector<uint8_t> &InputStream) = 0;

    /**
     * @brief Create a Route from waypoint list.
     *        The waypoint list must contain at least start point and destination point.
     *        All points must keep the order in the vector.
     *        The first point could be START type, otherwise current car position will be used as start.
     *        The last point must be DESTINATION type, if not, it will also be considered as destination for fallback reason.
     *        The others(if any) will be used with its order in the vector.
     * @param Waypoints     Require at least two points
     * @return HDRouteID    Represent a HD route
     */
    virtual HDRouteID CreateRoute(const std::vector<WayPoint> &Waypoints) = 0;

    /**
     * @brief Release a HD route including link sequence.
     * 
     * @param RouteID   Represent a HD route
     * @return true     Success
     * @return false    Failed
     */
    virtual bool ReleaseRoute(HDRouteID RouteID) = 0;

    /**
     * @brief Add avoid area to a route planning.
     * @param AvoidAreaUnit   Avoid area.
     * @return uint32_t   AreaID. 0 means failed, others valid value.
     */
    virtual uint32_t AddAvoidArea(AvoidAreaUnit Area) = 0;

    /**
     * @brief Get the Avoid Area List object
     * 
     * @return std::shared_ptr<AvoidAreaList> Pointer to the list
     */
    virtual std::shared_ptr<AvoidAreaList> GetAvoidAreaList() = 0;

    /**
     * @brief Remove an avoid area from a route.
     * 
     * @param AreaID    ID from AddAvoidArea()
     * @return true     Success
     * @return false    Failed
     */
    virtual bool RemoveAvoidArea(uint32_t AreaID) = 0;

    /**
     * @brief Set the planning options
     * 
     * @param RouteID               Represent a HD route
     * @param PlanningOptions       Planning options
     * @return true                 Success
     * @return false                Failed
     */
    virtual bool SetPlanningOptions(HDRouteID RouteID, HDRoutePlanningOptions PlanningOptions) = 0;

    /**
     * @brief Start HD route planning.
     * 
     * @param RouteID       Represent a HD route
     * @param NotifyResult  Function callback when result is ready.
     * @return true         Success to schedule async call
     * @return false        Failed to schedule async call
     */
    virtual bool StartPlanning(HDRouteID RouteID, std::function<void(HDRoutePlanningResult, std::shared_ptr<HDRouteInfo>)> NotifyResult) = 0;

    /**
     * @brief Stop a planning.
     * 
     * @param RouteID   Represent a HD route
     * @return true     Success
     * @return false    Failed
     */
    virtual bool StopPlanning(HDRouteID RouteID) = 0;
};


/**
 * @brief Status of lane planning progress.
 * 
 */
enum LanePlanningStatus
{
    LANE_PLANNING_STATUS_UNKNOWN = 0,        ///< default value means nothing.
    LANE_PLANNING_STATUS_CONTINUE,           ///< has continue information.
    LANE_PLANNING_STATUS_LAST,                ///< this is the last one
	LANE_PLANNING_STATUS_FAILED               ///< calc lane failed
};

/**
 * @brief Represent a lane
 * 
 */
struct LaneID
{
    DirectedLinkID m_LinkID;   ///< Link of this lane belongs to.
    uint16_t m_LaneGroupID;    ///< Lane group ID
    uint16_t m_LaneNum;        ///< Lane number
};

struct HDLaneSegmentCandidate
{
    LaneID m_LaneID;              ///< ID of this lane.
    uint16_t m_CandidateLevel;    ///< higher means more recommended.
};

struct HDLaneSegment
{
    LaneID m_LaneID;                                    ///< ID of this lane.
    float m_From;                                       ///< percentage of this lane group to start from.
    float m_To;                                         ///< percentage of this lane group to end to.
    std::vector<HDLaneSegmentCandidate> m_Candidates;   ///< candidates along with this lane.
};

/**
 * @brief Store a lane sequence of a part of route.
 * 
 */
struct HDLaneInfo
{
    HDRouteID m_RouteID;                         ///< the route based on.
    LanePlanningStatus m_Status;                 ///< status of planning progress.
    DirectedLinkID  m_StartLink;                 ///< start link of this part.
    DirectedLinkID  m_EndLink;                   ///< end link of this part.
    std::vector<HDLaneSegment> m_LaneSequence;   ///< lane sequence of this part.
};

/**
 * @brief Some "current" information on a certain lane.
 * 
 */
struct HDOnLaneInfo
{
    LaneID   m_LaneID;      ///< ID of this lane
    Pos3D    m_Position;    ///< Current Position of this lane. Should be the projection of current position to middle line.
};

class HDLanePlanning
{
protected:
    virtual ~HDLanePlanning() {};

public:
    /**
     * @brief Import a HD route with ID from HDRoutePlanning
     * 
     * @param RouteID   Represent a HD route
     * @return true     Success
     * @return false    Failed
     */
    virtual bool ImportHDRoute(HDRouteID RouteID) = 0;

    /**
     * @brief Import a HD route with link sequence
     * 
     * @param HDLinkSequence  Link sequence
     * @param LastLinkOffset Specify the offset of last link.
     * @return true     Success
     * @return false    Failed
     */
    virtual bool ImportHDRoute(std::vector<DirectedLinkID> &HDLinkSequence, uint32_t LastLinkOffset) = 0;

    /**
     * @brief Start lane planning based on imported HD route.
     *        This will start to plan a certain distance(configurable) and return result.
     *        Need to call this again if want to continue.
     * @param StartPosition     Specify current position.
     * @param NotifyProgress    Function callback to notify progress.
     * @return true             Planning is successfully scheduled.
     * @return false            Failed to schedule planning.
     */
    virtual bool StartPlanning(HDOnLaneInfo StartPosition, std::function<void(std::shared_ptr<HDLaneInfo>)> NotifyProgress) = 0;

    /**
     * @brief Stop the ongoing planning.
     * 
     * @return true     Success
     * @return false    Failed
     */
    virtual bool StopPlanning() = 0;
};



} //namespace Planning
} //namespace Engine
} //namespace Navinfo

#endif //_HDPLANNING_HPP_